import * as dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
import { cutDown } from '../../src/hammer/timer.mts';

dayjs.extend(durationPlugin);

describe('cutDown 倒计时函数', () => {
  beforeAll(() => {
    // Mock setTimeout and clearTimeout to control time in tests
    vi.useFakeTimers();
  });

  afterAll(() => {
    // Restore real timers
    vi.useRealTimers();
  });

  it('应该按照指定次数调用回调函数', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({ times: 3, callback, stepDuration: duration });

    // Fast-forward until all timers are executed
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();

    expect(callback).toHaveBeenCalledTimes(3);
    expect(callback).toHaveBeenNthCalledWith(1, 1);
    expect(callback).toHaveBeenNthCalledWith(2, 2);
    expect(callback).toHaveBeenNthCalledWith(3, 3);

    // Clean up
    cancel();
  });

  it('应该支持 immediate 选项', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({
      times: 3,
      callback,
      stepDuration: duration,
      immediate: true,
    });

    // First call should happen immediately
    expect(callback).toHaveBeenCalledTimes(1);
    expect(callback).toHaveBeenNthCalledWith(1, 1);

    // Advance timers for the remaining calls
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();

    expect(callback).toHaveBeenCalledTimes(3);
    expect(callback).toHaveBeenNthCalledWith(2, 2);
    expect(callback).toHaveBeenNthCalledWith(3, 3);

    cancel();
  });

  it('应该在指定次数后停止', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({ times: 2, callback, stepDuration: duration });

    // Advance more timers than needed
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();

    // Should only be called twice
    expect(callback).toHaveBeenCalledTimes(2);
    expect(callback).toHaveBeenNthCalledWith(1, 1);
    expect(callback).toHaveBeenNthCalledWith(2, 2);

    cancel();
  });

  it('应该支持 runappend 选项', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({
      times: 2,
      callback,
      stepDuration: duration,
      runappend: true,
    });

    // Advance timers more than the specified times
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();

    // Should continue to be called even after times is exceeded
    expect(callback).toHaveBeenCalledTimes(4);
    expect(callback).toHaveBeenNthCalledWith(1, 1);
    expect(callback).toHaveBeenNthCalledWith(2, 2);
    expect(callback).toHaveBeenNthCalledWith(3, 3);
    expect(callback).toHaveBeenNthCalledWith(4, 4);

    cancel();
  });

  it('应该使用默认的1秒间隔', () => {
    const callback = vi.fn();
    const cancel = cutDown({ times: 2, callback });

    // Advance by 1 second
    vi.advanceTimersByTime(1000);
    vi.advanceTimersByTime(1000);

    expect(callback).toHaveBeenCalledTimes(2);

    cancel();
  });

  it('应该返回一个可以停止计时器的取消函数', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({ times: 5, callback, stepDuration: duration });

    // Advance one timer
    vi.advanceTimersToNextTimer();
    expect(callback).toHaveBeenCalledTimes(1);

    // Cancel the timer
    cancel();

    // Advance more timers - no more callbacks should be called
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();

    // Still only called once
    expect(callback).toHaveBeenCalledTimes(1);
  });

  it('应该在 times 为 1 时正确执行', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({ times: 1, callback, stepDuration: duration });

    vi.advanceTimersToNextTimer();

    expect(callback).toHaveBeenCalledTimes(1);
    expect(callback).toHaveBeenNthCalledWith(1, 1);

    cancel();
  });

  it('应该验证times参数的有效性', () => {
    expect(() => {
      cutDown({ times: 0, callback: () => {} });
    }).toThrow('times must be greater than 0');

    expect(() => {
      cutDown({ times: -1, callback: () => {} });
    }).toThrow('times must be greater than 0');
  });

  it('应该验证stepDuration参数的有效性', () => {
    expect(() => {
      cutDown({ times: 1, stepDuration: dayjs.duration(0, 'ms') });
    }).toThrow('stepDuration must be greater than 0');

    expect(() => {
      cutDown({ times: 1, stepDuration: dayjs.duration(-100, 'ms') });
    }).toThrow('stepDuration must be greater than 0');
  });

  it('应该在回调中调用取消函数时正常工作', () => {
    const callback = vi.fn();
    const cancelRef = { current: undefined as (() => void) | undefined };

    const mockCallback = vi.fn((times: number) => {
      callback(times);
      if (times === 2 && cancelRef.current) {
        cancelRef.current(); // 在回调中取消
      }
    });

    cancelRef.current = cutDown({
      times: 5,
      callback: mockCallback,
      stepDuration: dayjs.duration(100, 'ms'),
    });

    vi.advanceTimersToNextTimer(); // 第1次
    vi.advanceTimersToNextTimer(); // 第2次，在这里被取消
    vi.advanceTimersToNextTimer(); // 第3次，不应该执行

    expect(callback).toHaveBeenCalledTimes(2);
  });

  it('应该在duration为0时不执行任何回调（包括immediate）', () => {
    const callback = vi.fn();
    const cancel = cutDown({
      times: 5,
      callback,
      stepDuration: dayjs.duration(100, 'ms'),
      duration: dayjs.duration(0, 'ms'),
      immediate: true,
    });

    // 即使是immediate，也不应该执行
    expect(callback).not.toHaveBeenCalled();

    vi.advanceTimersToNextTimer();
    expect(callback).not.toHaveBeenCalled();

    cancel();
  });

  it('应该在 immediate 为 true 且 times 为 1 时只执行一次', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({
      times: 1,
      callback,
      stepDuration: duration,
      immediate: true,
    });

    // Should be called immediately, and not scheduled again
    expect(callback).toHaveBeenCalledTimes(1);
    expect(callback).toHaveBeenNthCalledWith(1, 1);

    // Advance timer - should not be called again
    vi.advanceTimersToNextTimer();

    expect(callback).toHaveBeenCalledTimes(1);

    cancel();
  });

  it('应该支持不同的 duration 值', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(500, 'ms'); // 500ms
    const cancel = cutDown({ times: 2, callback, stepDuration: duration });

    // Advance by 500ms
    vi.advanceTimersByTime(500);
    vi.advanceTimersByTime(500);

    expect(callback).toHaveBeenCalledTimes(2);

    cancel();
  });

  it('应该在没有提供回调函数时正常运行', () => {
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({ times: 2, stepDuration: duration });

    // Should not throw any errors
    expect(() => {
      vi.advanceTimersToNextTimer();
      vi.advanceTimersToNextTimer();
    }).not.toThrow();

    cancel();
  });

  it('应该处理回调函数抛出异常的情况', () => {
    const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
    const callback = vi.fn().mockImplementation(() => {
      throw new Error('Test error');
    });
    const duration = dayjs.duration(100, 'ms');

    cutDown({ times: 3, callback, stepDuration: duration });

    // 第一次调用应该触发错误处理
    vi.advanceTimersToNextTimer();
    expect(callback).toHaveBeenCalledTimes(1);
    expect(consoleSpy).toHaveBeenCalledWith(
      'Error in cutDown callback:',
      expect.any(Error),
    );

    // 后续不应该再调用
    vi.advanceTimersToNextTimer();
    expect(callback).toHaveBeenCalledTimes(1);

    consoleSpy.mockRestore();
  });

  it('应该在取消后停止所有后续执行', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(100, 'ms');
    const cancel = cutDown({
      times: 10,
      callback,
      stepDuration: duration,
      runappend: true,
    });

    // 执行部分
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    expect(callback).toHaveBeenCalledTimes(2);

    // 取消
    cancel();

    // 确保不再执行
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    vi.advanceTimersToNextTimer();
    expect(callback).toHaveBeenCalledTimes(2);
  });

  it('应该处理极短的duration', () => {
    const callback = vi.fn();
    const duration = dayjs.duration(1, 'ms');
    const cancel = cutDown({ times: 3, callback, stepDuration: duration });

    vi.advanceTimersByTime(3);
    expect(callback).toHaveBeenCalledTimes(3);
    cancel();
  });

  it('应该支持duration参数限制总时长', () => {
    const callback = vi.fn();
    const stepDuration = dayjs.duration(100, 'ms');
    const totalDuration = dayjs.duration(250, 'ms'); // 总时长250ms

    // 模拟Date.now()随时间推进
    const startTime = 1000000000; // 固定的起始时间
    let currentMockTime = startTime;

    const dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => {
      return currentMockTime;
    });

    const cancel = cutDown({
      times: 10, // 理论上可以执行10次
      callback,
      stepDuration,
      duration: totalDuration,
    });

    // 第一次执行（在100ms时）
    currentMockTime += 100;
    vi.advanceTimersToNextTimer();

    // 第二次执行（在200ms时）
    currentMockTime += 100;
    vi.advanceTimersToNextTimer();

    // 第三次尝试执行（在300ms时，应该被250ms时长限制阻止）
    currentMockTime += 100;
    vi.advanceTimersToNextTimer();

    // 应该在总时长限制内停止，不会执行完10次
    expect(callback).toHaveBeenCalledTimes(2);
    cancel();
    dateNowSpy.mockRestore();
  });

  it('应该优先duration限制而不是times次数', () => {
    const callback = vi.fn();
    const stepDuration = dayjs.duration(50, 'ms');
    const totalDuration = dayjs.duration(120, 'ms');

    // 模拟Date.now()随时间推进
    const startTime = 1000000000; // 固定的起始时间
    let currentMockTime = startTime;

    const dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => {
      return currentMockTime;
    });

    const cancel = cutDown({
      times: 5,
      callback,
      stepDuration,
      duration: totalDuration,
    });

    // 第一次执行（在50ms时）
    currentMockTime += 50;
    vi.advanceTimersToNextTimer();

    // 第二次执行（在100ms时）
    currentMockTime += 50;
    vi.advanceTimersToNextTimer();

    // 第三次尝试执行（在150ms时，应该被120ms时长限制阻止）
    currentMockTime += 50;
    vi.advanceTimersToNextTimer();

    // 应该在120ms时长限制内停止，而不是执行5次
    expect(callback).toHaveBeenCalledTimes(2); // 50ms + 50ms = 100ms，第三次在150ms
    cancel();
    dateNowSpy.mockRestore();
  });

  it('应该处理duration为0的情况', () => {
    const callback = vi.fn();
    const cancel = cutDown({
      times: 5,
      callback,
      stepDuration: dayjs.duration(100, 'ms'),
      duration: dayjs.duration(0, 'ms'),
    });

    // 立即停止，不执行任何回调
    vi.advanceTimersToNextTimer();
    expect(callback).not.toHaveBeenCalled();
    cancel();
  });

  // 性能和并发测试
  describe('性能和并发测试', () => {
    it('应该处理大量并发调用而不造成内存泄漏', () => {
      const callbacks: Array<() => void> = [];
      const timers: Array<() => void> = [];

      // 创建100个并发的计时器
      for (let i = 0; i < 100; i++) {
        const callback = vi.fn();
        callbacks.push(callback);

        const cancel = cutDown({
          times: 3,
          callback,
          stepDuration: dayjs.duration(10, 'ms'),
        });
        timers.push(cancel);
      }

      // 执行一些计时器
      vi.advanceTimersByTime(30);

      // 验证所有回调都被正确调用
      callbacks.forEach((callback) => {
        expect(callback).toHaveBeenCalledTimes(3);
      });

      // 清理所有计时器
      timers.forEach((cancel) => cancel());
    });

    it('应该在高频率调用时保持时间精度', () => {
      const callback = vi.fn();
      const startTime = 1000000000;
      let currentTime = startTime;

      const dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => {
        return currentTime;
      });

      const cancel = cutDown({
        times: 1000,
        callback,
        stepDuration: dayjs.duration(1, 'ms'),
        duration: dayjs.duration(100, 'ms'), // 100ms总时长
      });

      // 模拟100ms的执行
      for (let i = 0; i < 100; i++) {
        currentTime += 1;
        vi.advanceTimersToNextTimer();
      }

      // 应该执行约99-100次（受时长限制和时间精度影响）
      expect(callback.mock.calls.length).toBeGreaterThanOrEqual(99);
      expect(callback.mock.calls.length).toBeLessThanOrEqual(100);

      cancel();
      dateNowSpy.mockRestore();
    });

    it('应该正确处理快速取消操作', () => {
      const callbacks: (() => void)[] = [];
      const cancels: (() => void)[] = [];

      // 创建并立即取消多个计时器
      for (let i = 0; i < 50; i++) {
        const callback = vi.fn();
        callbacks.push(callback);

        const cancel = cutDown({
          times: 10,
          callback,
          stepDuration: dayjs.duration(100, 'ms'),
        });

        cancels.push(cancel);

        // 立即取消
        if (i % 2 === 0) {
          cancel();
        }
      }

      // 推进时间
      vi.advanceTimersToNextTimer();

      // 验证只有未取消的计时器执行了回调
      callbacks.forEach((callback, index) => {
        if (index % 2 === 0) {
          expect(callback).not.toHaveBeenCalled();
        } else {
          expect(callback).toHaveBeenCalledTimes(1);
        }
      });

      // 清理剩余的计时器
      cancels.forEach((cancel) => cancel());
    });
  });

  // 内存管理测试
  describe('内存管理测试', () => {
    it('应该在正常结束后清理所有计时器', () => {
      const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
      const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');

      const callback = vi.fn();
      const cancel = cutDown({
        times: 3,
        callback,
        stepDuration: dayjs.duration(100, 'ms'),
      });

      // 执行所有计时器
      vi.advanceTimersToNextTimer();
      vi.advanceTimersToNextTimer();
      vi.advanceTimersToNextTimer();

      expect(callback).toHaveBeenCalledTimes(3);

      // 验证clearTimeout被调用，说明计时器被清理
      expect(clearTimeoutSpy).toHaveBeenCalled();

      cancel();
      setTimeoutSpy.mockRestore();
      clearTimeoutSpy.mockRestore();
    });

    it('应该在手动取消后清理所有计时器', () => {
      const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');

      const callback = vi.fn();
      const cancel = cutDown({
        times: 10,
        callback,
        stepDuration: dayjs.duration(100, 'ms'),
      });

      // 执行部分计时器
      vi.advanceTimersToNextTimer();
      vi.advanceTimersToNextTimer();

      expect(callback).toHaveBeenCalledTimes(2);

      // 手动取消
      cancel();

      // 验证clearTimeout被调用
      expect(clearTimeoutSpy).toHaveBeenCalled();

      // 验证后续不再执行
      vi.advanceTimersToNextTimer();
      expect(callback).toHaveBeenCalledTimes(2);

      clearTimeoutSpy.mockRestore();
    });

    it('应该在回调抛出异常后清理计时器', () => {
      const consoleSpy = vi
        .spyOn(console, 'error')
        .mockImplementation(() => {});
      const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');

      const callback = vi.fn().mockImplementation(() => {
        throw new Error('Test error');
      });

      const cancel = cutDown({
        times: 5,
        callback,
        stepDuration: dayjs.duration(100, 'ms'),
      });

      // 第一次执行应该抛出异常并清理计时器
      vi.advanceTimersToNextTimer();

      expect(callback).toHaveBeenCalledTimes(1);
      expect(consoleSpy).toHaveBeenCalled();
      expect(clearTimeoutSpy).toHaveBeenCalled();

      // 后续不应该再执行
      vi.advanceTimersToNextTimer();
      expect(callback).toHaveBeenCalledTimes(1);

      cancel();
      consoleSpy.mockRestore();
      clearTimeoutSpy.mockRestore();
    });
  });
});
